package jpave.query_interface;

/**********************************************************
* used by both Basic Sequence Query and Basic DB Hits Query
* 	just the bottom buttons of Delete All, Unselect, etc
* 	the search also goes through here
* The top line of the panel is in BasicQueryTab
* Other parts are in BasicPromptPanel 
* 
* Initiaties the queries where are in JPDBWrapper
***************************************************************/
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;
import java.io.File;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.PrintWriter;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.JScrollPane;
import javax.swing.JOptionPane;

import util.Debug.ErrorReport;
import util.ui.DisplayFloat;

import jpave.dataholders.SessionData;
import jpave.manager.ManagerFrame;

public class BasicQueryResultListPanel extends JPanel { 
	private static final long serialVersionUID = -3106579535544204890L;
	private static final int MAIN_PANEL_LABEL_WIDTH = 100;
	private static final Color BGCOLOR = ManagerFrame.BGCOLOR;

	public BasicQueryResultListPanel(BasicQueryTab parentTab, SessionData assemblies) {
		theAssemblies = assemblies;
		theParentTab = parentTab;
		
		super.setBackground(BGCOLOR);
		queryData = new ListQueryData();
		
		btnDelete = new JButton("Delete selected");
		btnDelete.setMargin(new Insets(0, 0, 0, 0));
		btnDelete.setFont(new Font(btnDelete.getFont().getName(),Font.PLAIN,10));
		btnDelete.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				deleteFromList();
				txtStatus.setText("");
				updateExportButton();
				updateCopyTableButton();
			}
		});
		
		btnKeep = new JButton("Keep selected");
		btnKeep.setMargin(new Insets(0, 0, 0, 0));
		btnKeep.setFont(new Font(btnKeep.getFont().getName(),Font.PLAIN,10));
		btnKeep.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				keepFromList();
				txtStatus.setText("");
				updateExportButton();
				updateCopyTableButton();
			}
		});

		JPanel topPanel = new JPanel();
		topPanel.setLayout(new BoxLayout ( topPanel, BoxLayout.X_AXIS ));
		topPanel.setBackground(Color.WHITE);

		resultList = new JTable();
		resultList.setColumnSelectionAllowed( false );
		resultList.setCellSelectionEnabled( false );
		resultList.setRowSelectionAllowed( true );
		resultList.setShowHorizontalLines( false );
		resultList.setShowVerticalLines( true );	
		resultList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		resultList.setIntercellSpacing ( new Dimension ( 1, 0 ) );	
		refreshTable();
		
		resultScroll = new JScrollPane(resultList,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

		resultScroll.setBorder( null );
		resultScroll.setPreferredSize(java.awt.Toolkit.getDefaultToolkit().getScreenSize()); // force table to use all space
		resultScroll.getViewport().setBackground(BGCOLOR);
		
		chkIncludeBuried = new JCheckBox("Include Buried Reads");
		chkIncludeBuried.setBackground(BGCOLOR);
		Dimension dim = chkIncludeBuried.getSize();
		dim.width = resultScroll.getMaximumSize().width;
		chkIncludeBuried.setSize(dim);
		
		checkESTPanel = new JPanel();
		checkESTPanel.setLayout(new BoxLayout ( checkESTPanel, BoxLayout.X_AXIS ));
		checkESTPanel.setBackground(BGCOLOR);
		checkESTPanel.add(chkIncludeBuried);
		checkESTPanel.setMaximumSize(checkESTPanel.getPreferredSize());
		
		if(theParentTab.getType() == BasicQueryTab.VIEW_BY_HIT)
			checkESTPanel.setVisible(false);
		
		JPanel optionPanel = new JPanel();
		optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS));
		optionPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		optionPanel.setBackground(BGCOLOR);
		optionPanel.add(checkESTPanel);
		optionPanel.setMaximumSize(optionPanel.getPreferredSize());
		
		txtStatus = new JTextField(400);
		txtStatus.setAlignmentX(CENTER_ALIGNMENT);
		txtStatus.setHorizontalAlignment(JTextField.CENTER);
		txtStatus.setEditable(false);
		txtStatus.setBackground(BGCOLOR);
		txtStatus.setBorder(BorderFactory.createEmptyBorder());
		Dimension dimStat = txtStatus.getPreferredSize();
		dimStat.width = 500;
		txtStatus.setPreferredSize(dimStat);
		txtStatus.setMaximumSize(txtStatus.getPreferredSize());

		btnDeleteAll = new JButton("Delete all");
		btnDeleteAll.setMargin(new Insets(0, 0, 0, 0));
		btnDeleteAll.setFont(new Font(btnDeleteAll.getFont().getName(),Font.PLAIN,10));
		btnDeleteAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				deleteAllFromList();
				txtStatus.setText("");
				updateExportButton();
				updateCopyTableButton();
			}
		});
		
		btnUnselectAll = new JButton("Unselect");
		btnUnselectAll.setMargin(new Insets(0, 0, 0, 0));
		btnUnselectAll.setFont(new Font(btnUnselectAll.getFont().getName(),Font.PLAIN,10));
		btnUnselectAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				unselectAll();
			}
		});
		
		lblHeader = new JLabel("Modify");
		dim = lblHeader.getPreferredSize();
		dim.width = MAIN_PANEL_LABEL_WIDTH;
		lblHeader.setPreferredSize(dim);
		lblHeader.setMaximumSize(lblHeader.getPreferredSize());
	
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout ( bottomPanel, BoxLayout.X_AXIS ));
		bottomPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		bottomPanel.setBackground(BGCOLOR);
		bottomPanel.setAlignmentY(LEFT_ALIGNMENT);
		bottomPanel.add(lblHeader);
		bottomPanel.add(btnDeleteAll);
		bottomPanel.add(Box.createHorizontalStrut(5));
		bottomPanel.add(btnUnselectAll);
		bottomPanel.add(Box.createHorizontalStrut(10));
		bottomPanel.add(btnDelete);
		bottomPanel.add(Box.createHorizontalStrut(5));
		bottomPanel.add(btnKeep);

		bottomPanel.add(Box.createHorizontalGlue());
		bottomPanel.setMaximumSize(bottomPanel.getPreferredSize());
		
		setLayout(new BoxLayout ( this, BoxLayout.Y_AXIS ));
		add(Box.createVerticalStrut(15));
		add(topPanel);
	
		add(optionPanel);
		add(Box.createVerticalStrut(10));
		add(txtStatus);
		add(Box.createVerticalStrut(10));
		add(resultScroll);
		add(Box.createVerticalStrut(10));
		add(bottomPanel);
		add(Box.createVerticalStrut(10));
		setMaximumSize(this.getPreferredSize());
		
		chkIncludeBuried.setVisible(false);
	}
	
	static public ArrayList<HitGroupListData> convertToGroupedHits(ArrayList<HitIndividualListData> hits) {
		ArrayList<HitGroupListData> retVal = new ArrayList<HitGroupListData> ();
		Iterator<HitIndividualListData> iter = hits.iterator();
		
		int rowNum = 0;
		
		while(iter.hasNext()) {
			HitIndividualListData item = iter.next();
			boolean found = false;
			for(int x=0; x<retVal.size() && !found; x++) {
				if(retVal.get(x).equals(item)) {
					retVal.get(x).incrementContigCount();
					retVal.get(x).addSetCounts(item.setCounts);
					found = true;
				}
			}
			if(!found) {
				HitGroupListData temp = item.getGroupItem();
				temp.setRowNum(++rowNum);
				retVal.add(temp);
			}
		}		
		return retVal;
	}
	
	static public boolean isResultInList(ArrayList<HitIndividualListData> theList, HitIndividualListData theData) {
		Iterator<HitIndividualListData> iter = theList.iterator();
		boolean found = false;
		
		while(!found && iter.hasNext()) {
			HitIndividualListData temp = iter.next();
			found = (temp.strHitName.equals(theData.strHitName) && 
					temp.strContig.equals(theData.strContig) && (temp.nRank == theData.nRank));
		}
		return found;
	}

	static public boolean isResultInList(ArrayList<ContigIndividualListData> theList, ContigIndividualListData theData) {
		Iterator<ContigIndividualListData> iter = theList.iterator();
		boolean found = false;
		
		while(!found && iter.hasNext()) {
			ContigIndividualListData temp = iter.next();
			found = (temp.strRead.equals(theData.strRead) && temp.strContig.equals(theData.strContig) && (temp.bIsBuried == theData.bIsBuried));
		}
		return found;
	}

	static public boolean isResultInList(ArrayList<ContigGroupListData> theList, ContigGroupListData theData) {
		Iterator<ContigGroupListData> iter = theList.iterator();
		boolean found = false;
		String notes = "";
		while(!found && iter.hasNext()) {
			ContigGroupListData temp = iter.next();
			if(temp.strNotes != null)
				notes = temp.strNotes;
			
			found = (notes.equals(theData.strNotes) && temp.strContig.equals(theData.strContig));
		}
		return found;
	}

	public int getNumRows() {
		return resultList.getRowCount(); 
	}
	
	public int getNextUniqueContig(int pos) {
		String temp = getContigAt(pos);
		int nextPos = getNextRowNum(pos);
		while(nextPos >= 0 && nextPos != pos && temp.equals(getContigAt(nextPos))) {
			nextPos = getNextRowNum(nextPos);
		}		
		return nextPos;
	}
	
	public int getPrevUniqueContig(int pos) {
		String temp = getContigAt(pos);
		int nextPos = getPrevRowNum(pos);
		while(nextPos >= 0 && nextPos != pos && temp.equals(getContigAt(nextPos))) {
			nextPos = getPrevRowNum(nextPos);
		}
		return nextPos;
	}
	
	//Used to copy table to the clipboard
    public String createTableString(String delim) {
    		StringBuilder retVal = new StringBuilder();
    	
		for(int x=0; x<resultList.getColumnCount()-1; x++) {
			retVal.append(resultList.getColumnName(x));
			retVal.append(delim);
		}
		retVal.append(resultList.getColumnName(resultList.getColumnCount()-1));
		retVal.append("\n");
		
		for(int x=0; x<resultList.getRowCount(); x++) {
			StringBuilder row = new StringBuilder();
			for(int y=0; y<resultList.getColumnCount()-1; y++) {
				row.append(resultList.getValueAt(x, y));
				row.append(delim);
			}
			retVal.append(row.toString());
			retVal.append(resultList.getValueAt(x, resultList.getColumnCount()-1));
			retVal.append("\n");
		}
		return retVal.toString();
    }
	
	private int getNextRowNum(int currentRow) {
		if(resultList.getRowCount() == 0) return -1;
		if(currentRow + 1 >= resultList.getRowCount())
			return 0;
		return currentRow + 1;
	}
	
	private int getPrevRowNum(int nextRow) {
		if(resultList.getRowCount() == 0) return -1;
		if(nextRow == 0 || nextRow >= resultList.getRowCount())
			return resultList.getRowCount() - 1;
		return nextRow - 1;
	}
	
	public void updateExportButton() {
		theParentTab.setExportEnabled(resultList.getRowCount() > 0);
	}
	
	public void updateCopyTableButton() {
		theParentTab.setCopyTableEnabled(resultList.getRowCount() > 0);
	}
	
	public void exportTableToFile() {
		saveTextToFile(theModel.getCSVExport());
	}
	
	private void saveTextToFile(String lines) {
		final JFileChooser fc = new JFileChooser(theParentTab.getParentFrame().lastSaveFilePath);
		fc.setSelectedFile(new File("DBhitTable.csv"));
		final String theLines = lines;
		if (fc.showSaveDialog(theParentTab) == JFileChooser.APPROVE_OPTION) {
			final File f = fc.getSelectedFile();

			int writeOption = JOptionPane.YES_OPTION;
			if (f.exists()) {
				writeOption = JOptionPane.showConfirmDialog(
					    theParentTab,
					    "The file already exists, overwrite it?",
					    "Save to File",
					    JOptionPane.YES_NO_OPTION);
			}
			if (writeOption == JOptionPane.YES_OPTION) {
				theParentTab.getParentFrame().setLastPath(f.getPath());
				Thread theThread = new Thread(new Runnable() {
					public void run() {
					    	PrintWriter pw = null;
					    	try {
					    		System.out.println("Writing to file " + f.getPath());
					    		pw = new PrintWriter(new BufferedWriter(new FileWriter(f)));
					    		pw.print(theLines);
					    		pw.close();
					    	}
					    	catch(Exception e) 
					    	  { ErrorReport.prtReport(e, "Writing to file " + f.getPath());}
					}
				});
				theThread.start();
			}
		}
	}
	
	public void setShowBuriedVisible(boolean visible) { chkIncludeBuried.setVisible(visible); }

	public void selectAll() { resultList.selectAll(); }
	public void unselectAll() { resultList.clearSelection(); }
		
	// XX initiaties queries
	public void addListFromQuery()
	{
		Thread thread = new Thread(new Runnable() {
		public void run() {
			try {
				//disable the button while thread is running
				theParentTab.setAddToTableEnabled(false);
				String searchStr = theParentTab.getSearchStr().replace('*', '%');
				// CAS 4July15 underscore is a special mysql character that matches anything
				searchStr = theParentTab.getSearchStr().replace("_", "\\_");
				String resultStr = "";
				if(theParentTab.getQueryMode() == BasicPromptPanel.QUERY_MODE_HIT_ID) {
					ArrayList<Object []> results = null;
					
					txtStatus.setText("Loading Hit IDs Please Wait..");
					results = theAssemblies.loadContigWithHitID(searchStr, theParentTab);
					if(results.isEmpty())
						results = theAssemblies.loadContigWithHitID("%" + searchStr + "%", theParentTab);
					
					int oldSize = queryData.getNumIndividualHitSearchResults();
					queryData.addIndividualHitSearch(results);
					int newSize = queryData.getNumIndividualHitSearchResults();
					queryData.generateGroupHitList();
					
					resultStr = "Results: " + newSize + " Hit IDs";
					if (oldSize != 0) resultStr += " (added " + (newSize-oldSize) + ")";
					resultStr += theParentTab.getHitSummary();
				}			
				else if(theParentTab.getQueryMode() == BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION) {
					ArrayList<Object []> results = null;
					
					txtStatus.setText("Loading Hit IDs Please Wait..");
					results = theAssemblies.loadContigWithHitDescription(searchStr, theParentTab);
					if(results.isEmpty())
						results = theAssemblies.loadContigWithHitDescription("%" + searchStr + "%", theParentTab);
					
					int oldSize = queryData.getNumIndividualDescriptionSearchResults();
					queryData.addIndividualDescriptionSearch(results);
					int newSize = queryData.getNumIndividualDescriptionSearchResults(); // WN this doesn't give right answer for grouped
					queryData.generateGroupDescriptionList();
					
					resultStr = "Results: " + newSize + " Seqs"; // CAS 2/3/15 
					if (oldSize != 0) resultStr += " (added " + (newSize-oldSize) + ")";
					resultStr += theParentTab.getHitSummary();
				}	
				else if(theParentTab.getQueryMode() == BasicPromptPanel.QUERY_MODE_CONTIG_READ) {
					ArrayList<Object []> results = null;
					txtStatus.setText("Loading Sequences Please Wait..");
					results = theAssemblies.loadCloneIDs(searchStr,  chkIncludeBuried.isSelected(), theParentTab.getResultLimit());
					if(results.isEmpty())
						results = theAssemblies.loadCloneIDs("%" + searchStr + "%", chkIncludeBuried.isSelected(), theParentTab.getResultLimit());
					int oldSize = queryData.getNumContigReadSearchResults();
					queryData.addContigReadSearch(results);
					int newSize = queryData.getNumContigReadSearchResults();
					
					resultStr = "Results: " + newSize + " sequences";
					if (oldSize != 0) resultStr += " (added " + (newSize-oldSize) + ")";
				}
				else if(theParentTab.getQueryMode() == BasicPromptPanel.QUERY_MODE_CONTIG_NAME) {
					ArrayList<Object []> results = new ArrayList<Object []> ();
					txtStatus.setText("Loading Seq IDs Please Wait..");
					results = theAssemblies.loadContigIDs(searchStr, theParentTab);
					
					if(results.isEmpty())
						results = theAssemblies.loadContigIDs("%" + searchStr + "%", theParentTab);

					int oldSize = queryData.getNumContigIDSearchResults();
					queryData.addContigIDSearch(results);
					int newSize = queryData.getNumContigIDSearchResults();
					
					resultStr = "Results: " + newSize + " Sequences";
					if (oldSize != 0) resultStr += " (added " + (newSize-oldSize) + ")";
				}
				else if(theParentTab.getQueryMode() == BasicPromptPanel.QUERY_MODE_CONTIG_NOTE) {
					ArrayList<Object []> results = new ArrayList<Object []> ();
					txtStatus.setText("Loading Remarks Please Wait..");
					results = theAssemblies.loadContigWithNotes(searchStr, theParentTab);

					if(results.isEmpty())
						results = theAssemblies.loadContigWithNotes("%" + searchStr + "%", theParentTab);

					int oldSize = queryData.getNumContigNoteSearchResults();
					queryData.addContigNoteSearch(results);
					int newSize = queryData.getNumContigNoteSearchResults();
					
					resultStr = "Results: " + newSize + " Remarks";
					if (oldSize != 0) resultStr += " (added " + (newSize-oldSize) + ")";
				}			
				theParentTab.update();
				theParentTab.setAddToTableEnabled(true);
				updateExportButton();
				updateCopyTableButton();
				txtStatus.setText(resultStr);	
			} catch (Exception err) {
				theParentTab.setAddToTableEnabled(true);
				ErrorReport.reportError(err, "Internal error: add to list");
			}
		}
		});
		thread.setPriority(Thread.MIN_PRIORITY);
		thread.start();
	}
	
	private void keepFromList()
	{
		int numElements = resultList.getRowCount();
		int [] selValues = resultList.getSelectedRows();		

		int [] opposite = new int[numElements - selValues.length];
		int x=0, selPos=0, nextval=0;
		
		for(x=0; x<opposite.length; x++)
		{
			while(selPos<selValues.length && selValues[selPos] == nextval) {
				selPos++;
				nextval++;
			}
			opposite[x] = nextval++;
		}
		deleteFromList(opposite);
	}
	
	private void deleteFromList()
	{
		int [] selValues = resultList.getSelectedRows();
		deleteFromList(selValues);
	}
	
	private void deleteAllFromList() {
		int qMode = theParentTab.getQueryMode();
		for(int x=resultList.getModel().getRowCount()-1; x>= 0; x--)
			deleteFromList(qMode, x);
		resultList.removeAll();
		if(qMode == BasicPromptPanel.QUERY_MODE_HIT_ID)
			queryData.generateGroupHitList();
		else if(qMode == BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION)
			queryData.generateGroupDescriptionList();
		
		refreshTable();
	}
	
	private void deleteFromList(int [] selValues)
	{
		int qMode = theParentTab.getQueryMode();
		resultList.clearSelection();
		for(int x=selValues.length-1; x>=0; x--) {
			deleteFromList(qMode, selValues[x]);
		}
		if(qMode == BasicPromptPanel.QUERY_MODE_HIT_ID)
			queryData.generateGroupHitList();
		else if(qMode == BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION)
			queryData.generateGroupDescriptionList();
		
		refreshTable();
	}
	
	private void deleteFromList(int qMode, int pos) {
		switch(qMode) {
		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
			if(!theParentTab.isGroupedSelected())
				queryData.hitSearchRemoveAt(pos);
			else
				queryData.hitSearchRemoveByGroupHitIDAt(pos);
			break;
		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION: 
			if(!theParentTab.isGroupedSelected())
				queryData.descriptionSearchRemoveAt(pos);
			else
				queryData.descriptionSearchRemoveByGroupHitIDAt(pos);
				break;
		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		
			queryData.contigIDSearchRemoveAt(pos);
			break;
		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		
			queryData.contigReadSearchRemoveAt(pos);
			break;
		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		
			queryData.contigNoteSearchRemoveAt(pos);
			break;
		}
	}
	
	public ArrayList<String> getContigList() {
		switch(theParentTab.getQueryMode()) {
		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			return queryData.getHitContigList();
		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	return queryData.getHitDescriptionContigList();
		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		return queryData.getContigIDContigList();
		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		return queryData.getContigReadContigList();
		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		return queryData.getContigNoteContigList();
		}
		return null;
	}
	
	public String getSelectedContig() {
		int pos = resultList.getSelectedRow();

		switch(theParentTab.getQueryMode()) {
		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			return queryData.getIndividualHitContigAt(pos);
		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	return queryData.getIndividualDescriptionContigAt(pos);
		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		return queryData.getContigIDContigAt(pos);
		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		return queryData.getContigReadContigAt(pos);
		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		return queryData.getContigNoteContigAt(pos);
		}
		return null;		
	}
	
	public String getContigAt(int row) {
		switch(theParentTab.getQueryMode()) {
		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			return queryData.getIndividualHitContigAt(row);
		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	return queryData.getIndividualDescriptionContigAt(row);
		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		return queryData.getContigIDContigAt(row);
		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		return queryData.getContigReadContigAt(row);
		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		return queryData.getContigNoteContigAt(row);
		}
		return null;				
	}
	
	public String [] getSelectedContigs() {
		int [] selections = resultList.getSelectedRows();
		if(selections.length == 0) {
			selections = new int[resultList.getRowCount()];
			for(int x=0; x<selections.length; x++) selections[x] = x;
		}
		Vector<String> retVal = new Vector<String> ();
		for(int x=0; x<selections.length; x++) {
			String contigID = "";
			switch(theParentTab.getQueryMode()) {
			case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
				if(theParentTab.isGroupedSelected()) {
					String [] ids = queryData.getGroupHitContigAt(selections[x]);
					for(int idx=0; idx<ids.length; idx++)
						if(!retVal.contains(ids[idx]))
							retVal.add(ids[idx]);
				}
				else 
					contigID =  queryData.getIndividualHitContigAt(selections[x]);
				break;
			case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	
				if(theParentTab.isGroupedSelected()) {
					String [] ids = queryData.getGroupDescriptionContigAt(selections[x]);
					for(int idx=0; idx<ids.length; idx++)
						if(!retVal.contains(ids[idx]))
								retVal.add(ids[idx]);
				}
				else 
					contigID =  queryData.getIndividualDescriptionContigAt(selections[x]);
				break;
			case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		
				contigID = queryData.getContigIDContigAt(selections[x]);
			break;
			case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		
				contigID = queryData.getContigReadContigAt(selections[x]);
			break;
			case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		
				contigID = queryData.getContigNoteContigAt(selections[x]);
			break;
			}
			if(contigID.length() > 0 && !retVal.contains(contigID))
				retVal.add(contigID);
		}
		
		return retVal.toArray(new String[0]);
	}
	
	public String [] getSelectedHitIDs() {
		int [] selections = resultList.getSelectedRows();
		if(selections.length == 0) {
			selections = new int[resultList.getRowCount()];
			for(int x=0; x<selections.length; x++) selections[x] = x;
		}
		Vector<String> retVal = new Vector<String> ();
		for(int x=0; x<selections.length; x++) {
			String contigID = "";
			switch(theParentTab.getQueryMode()) {
			case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
				if(theParentTab.isGroupedSelected()) {
					String [] ids = queryData.getGroupHitIDAt(selections[x]);
					for(int idx=0; idx<ids.length; idx++)
						if(!retVal.contains(ids[idx]))
							retVal.add(ids[idx]);
				}
				else 
					contigID =  queryData.getIndividualHitIDAt(selections[x]);
				break;
			case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	
				if(theParentTab.isGroupedSelected()) {
					String [] ids = queryData.getGroupDescriptionIDAt(selections[x]);
					for(int idx=0; idx<ids.length; idx++)
						if(!retVal.contains(ids[idx]))
								retVal.add(ids[idx]);
				}
				else 
					contigID =  queryData.getIndividualDescriptionIDAt(selections[x]);
				break;
			}
			if(contigID.length() > 0 && !retVal.contains(contigID))
				retVal.add(contigID);
		}
		
		return retVal.toArray(new String[0]);
	}

	public int getSelectedRowNum() { return resultList.getSelectedRow(); }

	public void refreshTable() {
		SwingUtilities.invokeLater(
			new Runnable() {
				public void run() {
					freeOldModel();
					theModel = new QueryResultsTableModel(theParentTab.getColumns(), 
							theParentTab.getColumnSelections());
					resultList.setModel(theModel);
					resizeColumns();
					JTableHeader header = resultList.getTableHeader();
					header.setUpdateTableInRealTime(true);
					colListener = theModel.new ColumnListener(resultList);
					header.addMouseListener(colListener);
					resultList.getTableHeader().setBackground(Color.WHITE);
					
					selListener = new SelectionListener(resultList, theParentTab);
					
					resultList.getSelectionModel().addListSelectionListener(selListener);
					resultList.getColumnModel().getSelectionModel().addListSelectionListener(selListener);
				}
			}
		);
	}
	
	public void addSelectionListener(SelectionListener selLis) {
		resultList.getSelectionModel().addListSelectionListener(selLis);
		resultList.getColumnModel().getSelectionModel().addListSelectionListener(selLis);		
	}
	
	private void freeOldModel() {
		if(theModel != null) {
			JTableHeader header = resultList.getTableHeader();
			header.removeMouseListener(colListener);
			resultList.getSelectionModel().removeListSelectionListener(selListener);
			resultList.getColumnModel().getSelectionModel().removeListSelectionListener(selListener);
		}
	}
	 
	private void resizeColumns() {
		if(resultList.getRowCount() > 0)
			resultList.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		else
			resultList.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
		
		int margin = 2;
		int totalWidth = 0;
		
		for(int x=0; x < resultList.getColumnCount(); x++) {
			DefaultTableColumnModel colModel = (DefaultTableColumnModel) resultList.getColumnModel();
			TableColumn col = colModel.getColumn(x);
			int maxSize = 0;
			
			TableCellRenderer renderer = col.getHeaderRenderer();
			
			if(renderer == null)
				renderer = resultList.getTableHeader().getDefaultRenderer();
			
			Component comp = renderer.getTableCellRendererComponent(resultList, 
					col.getHeaderValue(), false, false, 0, 0);
			maxSize = comp.getPreferredSize().width;
			
			for(int y=0; y<resultList.getRowCount(); y++) {
				renderer = resultList.getCellRenderer(y, x);
				comp = renderer.getTableCellRendererComponent(resultList, 
						resultList.getValueAt(y, x), false, false, y, x);
				maxSize = Math.max(maxSize, comp.getPreferredSize().width);
			}
			maxSize += 2 * margin;
			
			totalWidth += maxSize;
			col.setPreferredWidth(maxSize);
		}
		
		if(resultScroll != null && totalWidth < resultScroll.getWidth()) {
			DefaultTableColumnModel colModel = (DefaultTableColumnModel) resultList.getColumnModel();
			TableColumn col = colModel.getColumn(resultList.getColumnCount() - 1);

			col.setPreferredWidth(col.getPreferredWidth() + (resultScroll.getWidth() - totalWidth));
		}
		
		((DefaultTableCellRenderer) resultList.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(SwingConstants.LEFT);
	}
	
	//User interface
	private JButton btnDelete = null, btnKeep = null;
	private JButton btnDeleteAll = null, btnUnselectAll = null;
	private JLabel lblHeader = null;
	private JTextField txtStatus = null;
	private JScrollPane resultScroll = null;
	private JCheckBox chkIncludeBuried = null;
	private JPanel checkESTPanel = null;
	private JTable resultList = null;

	//Data Holders
	private BasicQueryTab theParentTab;
	private SessionData theAssemblies = null;
	private ListQueryData queryData = null;
	private QueryResultsTableModel theModel = null;
	private QueryResultsTableModel.ColumnListener colListener = null;
	private SelectionListener selListener = null;
	/**
	 * Used to organize/store basic query results 
	 */
	private class ListQueryData
	{
		ListQueryData()
		{
			resultIndividualHitList = new ArrayList<HitIndividualListData> (); 
			resultGroupHitList = new ArrayList<HitGroupListData> ();
			resultIndividualDescriptionList = new ArrayList<HitIndividualListData> (); 
			resultGroupDescriptionList = new ArrayList<HitGroupListData> ();
			resultContigIDList = new ArrayList<ContigGroupListData> ();
			resultContigReadList = new ArrayList<ContigIndividualListData> ();
			resultContigNoteList = new ArrayList<ContigGroupListData> ();
		}
		
		public void generateGroupHitList() { resultGroupHitList = convertToGroupedHits(resultIndividualHitList); }
		public void generateGroupDescriptionList() { resultGroupDescriptionList = convertToGroupedHits(resultIndividualDescriptionList); }
		
		public void addIndividualHitSearch(ArrayList<Object []> newResults) {
			Iterator<Object []> objIter = newResults.iterator();
			
			while(objIter.hasNext())
				addIndividualHitSearch(new HitIndividualListData(objIter.next()));
		}

		public void addIndividualDescriptionSearch(ArrayList<Object []> newResults) {
			Iterator<Object []> objIter = newResults.iterator();
			
			while(objIter.hasNext())
				addIndividualDescriptionSearch(new HitIndividualListData(objIter.next()));
		}
				
		public void addContigIDSearch(ArrayList<Object []> newResults) {
			Iterator<Object []> objIter = newResults.iterator();
			
			while(objIter.hasNext())
				addContigIDSearch(new ContigGroupListData(objIter.next()));
		}
				
		public void addContigReadSearch(ArrayList<Object []> newResults) {
			Iterator<Object []> objIter = newResults.iterator();
			
			while(objIter.hasNext())
				addContigReadSearch(new ContigIndividualListData(objIter.next()));
		}
				
		public void addContigNoteSearch(ArrayList<Object []> newResults) {
			Iterator<Object []> objIter = newResults.iterator();
			
			while(objIter.hasNext())
				addContigNoteSearch(new ContigGroupListData(objIter.next()));
		}
				
		private void addIndividualHitSearch(HitIndividualListData newData) {
			if(resultIndividualHitList.size() == 0)
				nIndividualHitListIndex = 0;
			if((resultIndividualHitList.size() < theParentTab.getResultLimit()) && 
					(!isResultInList(resultIndividualHitList, newData))) {
				newData.setRowNum(++nIndividualHitListIndex);
				resultIndividualHitList.add(newData);
			}
		}
				
		private void addIndividualDescriptionSearch(HitIndividualListData newData) {			
			if(resultIndividualDescriptionList.size() == 0)
				nIndividualDescriptionListIndex = 0;
			if((resultIndividualDescriptionList.size() < theParentTab.getResultLimit()) && 
					(!isResultInList(resultIndividualDescriptionList, newData))) {
				newData.setRowNum(++nIndividualDescriptionListIndex);
				resultIndividualDescriptionList.add(newData);
			}
		}

		private void addContigIDSearch(ContigGroupListData newData) {			
			if(resultContigIDList.size() == 0)
				nContigIDListIndex = 0;
			if((resultContigIDList.size() < theParentTab.getResultLimit()) && 
					(!isResultInList(resultContigIDList, newData))) {
				newData.setRowNum(++nContigIDListIndex);
				resultContigIDList.add(newData);
			}
		}

		private void addContigReadSearch(ContigIndividualListData newData) {			
			if(resultContigReadList.size() == 0)
				nContigReadListIndex = 0;
			if((resultContigReadList.size() < theParentTab.getResultLimit()) && 
					(!isResultInList(resultContigReadList, newData))) {
				newData.setRowNum(++nContigReadListIndex);
				resultContigReadList.add(newData);
			}
		}

		private void addContigNoteSearch(ContigGroupListData newData) {			
			if(resultContigNoteList.size() == 0)
				nContigNoteListIndex = 0;
			if((resultContigNoteList.size() < theParentTab.getResultLimit()) && 
					(!isResultInList(resultContigNoteList, newData))) {
				newData.setRowNum(++nContigNoteListIndex);
				resultContigNoteList.add(newData);
			}
		}

		//Methods to remove selected results
		public void hitSearchRemoveAt(int pos) { resultIndividualHitList.remove(pos); }
		public void hitSearchRemoveByGroupHitIDAt(int pos) {
			String hitID = resultGroupHitList.get(pos).strHitName;
			for(int x= resultIndividualHitList.size()-1; x>=0; x--) {
				if(resultIndividualHitList.get(x).strHitName.equals(hitID))
					resultIndividualHitList.remove(x);
			}
		}
		public void descriptionSearchRemoveAt(int pos) { resultIndividualDescriptionList.remove(pos); }
		public void descriptionSearchRemoveByGroupHitIDAt(int pos) {
			String hitID = resultGroupDescriptionList.get(pos).strHitName;
			for(int x=resultIndividualDescriptionList.size()-1; x>=0; x--) {
				if(resultIndividualDescriptionList.get(x).strHitName.equals(hitID))
					resultIndividualDescriptionList.remove(x);
			}
			
		}
		public void contigIDSearchRemoveAt(int pos) { resultContigIDList.remove(pos); }
		public void contigReadSearchRemoveAt(int pos) { resultContigReadList.remove(pos); }
		public void contigNoteSearchRemoveAt(int pos) { resultContigNoteList.remove(pos); }
				
		public int getNumIndividualHitSearchResults() { return resultIndividualHitList.size(); }
		public int getNumGroupHitSearchResults() { return resultGroupHitList.size(); }
		public int getNumIndividualDescriptionSearchResults() { return resultIndividualDescriptionList.size(); }
		public int getNumGroupDescriptionSearchResults() { return resultGroupDescriptionList.size(); }
		public int getNumContigIDSearchResults() { return resultContigIDList.size(); }
		public int getNumContigReadSearchResults() { return resultContigReadList.size(); }
		public int getNumContigNoteSearchResults() { return resultContigNoteList.size(); }
		
		public Object getIndividualHitSearchResultAt(int row, int col) { return resultIndividualHitList.get(row).getValueAt(col); }
		public Object getGroupHitSearchResultAt(int row, int col) { return resultGroupHitList.get(row).getValueAt(col); }
		public Object getIndividualDescriptionSearchResultAt(int row, int col) { return resultIndividualDescriptionList.get(row).getValueAt(col); }
		public Object getGroupDescriptionSearchResultAt(int row, int col) { return resultGroupDescriptionList.get(row).getValueAt(col); }
		public Object getContigIDSearchResultAt(int row, int col) { return resultContigIDList.get(row).getValueAt(col); }
		public Object getContigReadSearchResultAt(int row, int col) { return resultContigReadList.get(row).getValueAt(col); }
		public Object getContigNoteSearchResultAt(int row, int col) { return resultContigNoteList.get(row).getValueAt(col); }
		
		public String getIndividualHitContigAt(int pos) { return resultIndividualHitList.get(pos).strContig; }
		public String [] getGroupHitContigAt(int pos) { 
			String hit = resultGroupHitList.get(pos).strHitName;
			Vector<String> ids = new Vector<String> ();
			for(int x=0; x<resultIndividualHitList.size(); x++) {
				if(resultIndividualHitList.get(x).strHitName.equals(hit)) {
					if(!ids.contains(resultIndividualHitList.get(x).strContig))
						ids.add(resultIndividualHitList.get(x).strContig);
				}
			}
			return ids.toArray(new String[0]);
		}
		public String getIndividualHitIDAt(int pos) { return resultIndividualHitList.get(pos).strHitName; }
		public String [] getGroupHitIDAt(int pos) { 
			String hit = resultGroupHitList.get(pos).strHitName;
			Vector<String> ids = new Vector<String> ();
			for(int x=0; x<resultIndividualHitList.size(); x++) {
				if(resultIndividualHitList.get(x).strHitName.equals(hit)) {
					if(!ids.contains(resultIndividualHitList.get(x).strHitName))
						ids.add(resultIndividualHitList.get(x).strHitName);
				}
			}
			
			return ids.toArray(new String[0]);
		}
		public String getIndividualDescriptionContigAt(int pos) { return resultIndividualDescriptionList.get(pos).strContig; }
		public String [] getGroupDescriptionContigAt(int pos) { 
			String hit = resultGroupDescriptionList.get(pos).strHitName;
			Vector<String> ids = new Vector<String> ();
			for(int x=0; x<resultIndividualDescriptionList.size(); x++) {
				if(resultIndividualDescriptionList.get(x).strHitName.equals(hit)) {
					if(!ids.contains(resultIndividualDescriptionList.get(x).strContig))
						ids.add(resultIndividualDescriptionList.get(x).strContig);
				}
			}
			
			return ids.toArray(new String[0]);
		}
		public String getIndividualDescriptionIDAt(int pos) { return resultIndividualDescriptionList.get(pos).strHitName; }
		public String [] getGroupDescriptionIDAt(int pos) { 
			String hit = resultGroupDescriptionList.get(pos).strHitName;
			Vector<String> ids = new Vector<String> ();
			for(int x=0; x<resultIndividualDescriptionList.size(); x++) {
				if(resultIndividualDescriptionList.get(x).strHitName.equals(hit)) {
					if(!ids.contains(resultIndividualDescriptionList.get(x).strHitName))
						ids.add(resultIndividualDescriptionList.get(x).strHitName);
				}
			}	
			return ids.toArray(new String[0]);
		}
		public String getContigIDContigAt(int pos) { return resultContigIDList.get(pos).strContig; }
		public String getContigReadContigAt(int pos) { return resultContigReadList.get(pos).strContig; }
		public String getContigNoteContigAt(int pos) { return resultContigNoteList.get(pos).strContig; }
		
		public ArrayList<String> getHitContigList() {
			Iterator<HitIndividualListData> hitIter = resultIndividualHitList.iterator();
			ArrayList<String> retVal = new ArrayList<String> ();
			
			while(hitIter.hasNext()) {
				String contigID = hitIter.next().strContig;
				if(!retVal.contains(contigID))
					retVal.add(contigID);
			}
			return retVal;
		}
		
		public ArrayList<String> getHitDescriptionContigList() {
			Iterator<HitIndividualListData> hitIter = resultIndividualDescriptionList.iterator();
			ArrayList<String> retVal = new ArrayList<String> ();
			
			while(hitIter.hasNext()) {
				String contigID = hitIter.next().strContig;
				if(!retVal.contains(contigID))
					retVal.add(contigID);
			}
			return retVal;
		}
		
		public ArrayList<String> getContigIDContigList() {
			Iterator<ContigGroupListData> hitIter = resultContigIDList.iterator();
			ArrayList<String> retVal = new ArrayList<String> ();
			
			while(hitIter.hasNext()) {
				String contigID = hitIter.next().strContig;
				if(!retVal.contains(contigID))
					retVal.add(contigID);
			}
			return retVal;
		}

		public ArrayList<String> getContigReadContigList() {
			Iterator<ContigIndividualListData> hitIter = resultContigReadList.iterator();
			ArrayList<String> retVal = new ArrayList<String> ();
			
			while(hitIter.hasNext()) {
				String read = hitIter.next().strContig;
				if(!retVal.contains(read))
					retVal.add(read);
			}
			return retVal;
		}
		
		public ArrayList<String> getContigNoteContigList() {
			Iterator<ContigGroupListData> hitIter = resultContigNoteList.iterator();
			ArrayList<String> retVal = new ArrayList<String> ();
			
			while(hitIter.hasNext()) {
				String read = hitIter.next().strContig;
				if(!retVal.contains(read))
					retVal.add(read);
			}
			return retVal;
		}

		private int nIndividualHitListIndex = 0, nIndividualDescriptionListIndex = 0, 
					nContigIDListIndex = 0, nContigReadListIndex = 0, nContigNoteListIndex = 0;
		private ArrayList<HitIndividualListData> resultIndividualHitList = null;
		private ArrayList<HitGroupListData> resultGroupHitList = null;
		private ArrayList<HitIndividualListData> resultIndividualDescriptionList = null;
		private ArrayList<HitGroupListData> resultGroupDescriptionList = null;
		private ArrayList<ContigGroupListData> resultContigIDList = null;
		private ArrayList<ContigIndividualListData> resultContigReadList = null;
		private ArrayList<ContigGroupListData> resultContigNoteList = null;
	}
	
	private class ContigGroupListData {
		public ContigGroupListData(Object [] values) {
			strContig = (String) values[0];
			strBestHit = (String) values[1];
			strBestDescrip = (String) values[2];
			strNotes = (String) values[3];
			nTotalExp = (Integer) values[4];
			strKEGG = (String) values[5];
			strPFAM = (String) values[6];
			strEC = (String) values[7];
			nSetCounts = new double [values.length - 8];
			for(int x=0; x<nSetCounts.length; x++)
				nSetCounts[x] = (Double) values[x+8];
		}
		
		public void setRowNum(int row) { nRowNum = row; }
		
		public Object getValueAt(int column) {
			switch(column) {
			case 0: return nRowNum;
			case 1: return strContig;
			case 2: return strBestHit;
			case 3: return strBestDescrip;
			case 4: return strNotes;
			case 5: return nTotalExp;
			case 6: return strKEGG;
			case 7: return strPFAM;
			case 8: return strEC;
			}
			return new DisplayFloat(nSetCounts[column-9]);
		}
		
		public int compareTo(ContigGroupListData obj, boolean ascOrder, int column) {
			int order = 1;
			if(!ascOrder) order = -1;
			
			switch(column) {
			case 0: return order * (new Integer(nRowNum)).compareTo(obj.nRowNum);
			case 1: return order * strContig.compareTo(obj.strContig);	
			case 2: return order * compareStrings(strBestHit, obj.strBestHit);
			case 3: return order * compareStrings(strBestDescrip, obj.strBestDescrip);
			case 4: return order * compareStrings(strNotes, obj.strNotes);
			case 5: return order * (new Integer(nTotalExp)).compareTo(obj.nTotalExp);
			case 6: return order * compareStrings(strKEGG, obj.strKEGG);
			case 7: return order * compareStrings(strPFAM, obj.strPFAM);
			case 8: return order * compareStrings(strEC, obj.strEC);
			}
			return order * ((new Double(nSetCounts[column-9])).compareTo(obj.nSetCounts[column-9]));
		}
		
		private int compareStrings(String str1, String str2) {
			if(str1==null && str2==null) return -1;
			if(str1==null) return -1;
			if(str2==null) return 1;
			return str1.compareTo(str2);
		}
		
		private int nRowNum = -1;
		private String strContig = "";
		private int nTotalExp = 0;
		private String strNotes = "";
		private String strBestHit = "";
		private String strBestDescrip = "";
		private String strKEGG = "";
		private String strPFAM = "";
		private String strEC = "";
		private double [] nSetCounts = null;
	}
	
	private class ContigIndividualListData { 
		public ContigIndividualListData(Object [] values) {
			strContig = (String) values[0];
			strRead = (String) values[1];
			bIsBuried = (Boolean) values[2];
		}
		
		public void setRowNum(int row) { nRowNum = row; }
		
		public Object getValueAt(int column) {
			switch(column) {
			case 0: return nRowNum;
			case 1: return strContig;
			case 2: return strRead;
			case 3: return bIsBuried;
			}
			return null;
		}
		
		public int compareTo(ContigIndividualListData obj, boolean ascOrder, int column) {
			int order = 1;
			if(!ascOrder) order *= -1;
			
			switch(column) {
			case 0: return order * (new Integer(nRowNum)).compareTo(obj.nRowNum);
			case 1: return order * strContig.compareTo(obj.strContig);
			case 2: return order * strRead.compareTo(obj.strRead);
			case 3: return order * (new Boolean(bIsBuried)).compareTo(obj.bIsBuried);
			}
			return 0;
		}	
		private int nRowNum = -1;
		private String strContig;
		private String strRead;
		private boolean bIsBuried;	
	}
	
	private class HitIndividualListData {
		public HitIndividualListData( Object [] values) {
			strContig = (String) values[0];
			strHitName = (String) values[1];
			dEVal = (Double) values[2];
			strDescription = (String) values[3];
			strSpecies = (String) values[4];
			strType = (String) values[5];
			strTaxonomy = (String) values[6];
			nRank = (Integer) values[7];
			nAlignmentLength = (Integer) values[8];
			nHitStart = (Integer) values[9];
			nHitEnd = (Integer) values[10];
			nContigStart = (Integer) values[11];
			nContigEnd = (Integer) values[12];
			strKEGG = (String) values[13];
			strPFAM = (String) values[14];
			strEC   = (String) values[15];
			// Shows all Sets and RPKM, where the first is
			// integer and the second is float. Maybe check type?
			setCounts = new int[values.length - 16];
			for(int x=0; x<setCounts.length; x++) {
				setCounts[x] = (Integer) values[16 + x];
			}
		}
		
		public void setRowNum(int row) { nRowNum = row; }
		
		public Object getValueAt(int column) {
			switch(column) {
			case 0: return nRowNum;
			case 1: return strContig;
			case 2: return strHitName;				
			case 3: return dEVal;
			case 4: return strDescription;
			case 5: return strSpecies;
			case 6: return strType;
			case 7: return strTaxonomy;
			case 8: return nRank;
			case 9: return nAlignmentLength;
			case 10: return nHitStart;
			case 11: return nHitEnd;
			case 12: return nContigStart;
			case 13: return nContigEnd;
			case 14: return strKEGG;
			case 15: return strPFAM;
			case 16: return strEC;
			}
			return setCounts[column - 17];
		}
		
		public int compareTo(HitIndividualListData obj, boolean ascOrder, int column) {
			int order = 1;
			if(!ascOrder) order = -1;
			
			switch(column) {
			case 0: return order * (new Integer(nRowNum)).compareTo(obj.nRowNum);
			case 1: return order * compareStrings(strContig, obj.strContig);
			case 2: return order * compareStrings(strHitName,obj.strHitName);
			case 3: return order * (new Double(dEVal)).compareTo(obj.dEVal);
			case 4: return order * compareStrings(strDescription, obj.strDescription);
			case 5: return order * compareStrings(strSpecies, obj.strSpecies);
			case 6: return order * compareStrings(strType, obj.strType);
			case 7: return order * compareStrings(strTaxonomy, obj.strTaxonomy);
			case 8: return order * (new Integer(nAlignmentLength)).compareTo(obj.nAlignmentLength);
			case 9: return order * (new Integer(nRank)).compareTo(obj.nRank);
			case 10: return order * (new Integer(nHitStart)).compareTo(obj.nHitStart);
			case 11: return order * (new Integer(nHitEnd)).compareTo(obj.nHitEnd);
			case 12: return order * (new Integer(nContigStart)).compareTo(obj.nContigStart);
			case 13: return order * (new Integer(nContigEnd)).compareTo(obj.nContigEnd);
			case 14: return order * compareStrings(strKEGG, obj.strKEGG);
			case 15: return order * compareStrings(strPFAM, obj.strPFAM);
			case 16: return order * compareStrings(strEC, obj.strEC);
			}
			return order * (new Integer(setCounts[column-16])).compareTo(obj.setCounts[column-16]);
		}
		
		public HitGroupListData getGroupItem() {
			return new HitGroupListData(this);
		}
		
		private int nRowNum = -1;
		private String strContig;
		private String strHitName;
		private int nRank;
		private int nHitStart;
		private int nHitEnd;
		private int nContigStart;
		private int nContigEnd;
		private int nAlignmentLength;
		private double dEVal;
		private String strDescription;
		private String strSpecies;
		private String strType;
		private String strTaxonomy;
		private String strKEGG = null;
		private String strPFAM = null;
		private String strEC = null;
		private int [] setCounts = null;
	}
	
	public class HitGroupListData implements Comparable<HitGroupListData> {
		public HitGroupListData(HitIndividualListData hit) {
			strHitName = hit.strHitName;
			nNumContigs = 1;
			strDescription = hit.strDescription;
			strSpecies = hit.strSpecies;
			strType = hit.strType;
			strTaxonomy = hit.strTaxonomy;
			nSetCounts = new int[hit.setCounts.length];
			strKEGG = hit.strKEGG;
			strPFAM = hit.strPFAM;
			strEC = hit.strEC;
			for(int x=0; x<nSetCounts.length; x++)
				nSetCounts[x] = hit.setCounts[x];
		}
		
		public void setRowNum(int row) { nRowNum = row; }
		
		public boolean equals(HitIndividualListData hit) {
			return compareStrings(strHitName, hit.strHitName) == 0;
		}
		
		public int compareTo(HitGroupListData obj) {
			return compareStrings(strHitName, obj.strHitName);
		}
		
		public Object getValueAt(int column) {
			switch(column) {
			case 0: return nRowNum;
			case 1: return strHitName;
			case 2: return nNumContigs;
			case 3: return strDescription;
			case 4: return strSpecies;
			case 5: return strType;
			case 6: return strTaxonomy;
			case 7: return strKEGG;
			case 8: return strPFAM;
			case 9: return strEC;
			}
			return nSetCounts[column-10];
		}

		public void incrementContigCount() { nNumContigs++; }
		public void addSetCounts(int [] counts) {
			for(int x=0; x<counts.length; x++)
				nSetCounts[x] += counts[x];
		}

		public int compareTo(HitGroupListData obj, boolean ascOrder, int column) {
			int order = 1;
			if(!ascOrder) order = -1;
			switch(column) {
			case 0: return order * (new Integer(nRowNum)).compareTo(obj.nRowNum);
			case 1: return order * compareStrings(strHitName, obj.strHitName);
			case 2: return order * (new Integer(nNumContigs)).compareTo(obj.nNumContigs);
			case 3: return order * compareStrings(strDescription, obj.strDescription);
			case 4: return order * compareStrings(strSpecies, obj.strSpecies);
			case 5: return order * compareStrings(strType, obj.strType);
			case 6: return order * compareStrings(strTaxonomy, obj.strTaxonomy);
			case 7: return order * compareStrings(strKEGG, obj.strKEGG);
			case 8: return order * compareStrings(strPFAM, obj.strPFAM);
			case 9: return order * compareStrings(strEC, obj.strEC);
			}
			return order * (new Integer(nSetCounts[column-10])).compareTo(obj.nSetCounts[column-10]);
		}
		
		private int nRowNum = -1;
		private String strHitName;
		private int nNumContigs;
		private String strDescription;
		private String strSpecies;
		private String strType;
		private String strTaxonomy;
		private String strKEGG = null;
		private String strPFAM = null;
		private String strEC = null;
		private int [] nSetCounts = null;
	}
	
	private class HitIndividualListComparator implements Comparator<HitIndividualListData> {
		public HitIndividualListComparator(boolean sortAsc, int mode) {
			bSortAsc = sortAsc;
			nMode = mode;
		}		
		public int compare(HitIndividualListData obj1, HitIndividualListData obj2) { return obj1.compareTo(obj2, bSortAsc, nMode); }
		
		private boolean bSortAsc;
		private int nMode;
	}
	
	private class HitGroupListComparator implements Comparator<HitGroupListData> {
		public HitGroupListComparator(boolean sortAsc, int mode) {
			bSortAsc = sortAsc;
			nMode = mode;
		}		
		public int compare(HitGroupListData obj1, HitGroupListData obj2) { return obj1.compareTo(obj2, bSortAsc, nMode); }
		
		private boolean bSortAsc;
		private int nMode;
	}

	private class ContigIndividualListComparator implements Comparator<ContigIndividualListData> {
		public ContigIndividualListComparator(boolean sortAsc, int mode) {
			bSortAsc = sortAsc;
			nMode = mode;
		}		
		public int compare(ContigIndividualListData obj1, ContigIndividualListData obj2) { return obj1.compareTo(obj2, bSortAsc, nMode); }
		
		private boolean bSortAsc;
		private int nMode;
	}
	
	private class ContigGroupListComparator implements Comparator<ContigGroupListData> {
		public ContigGroupListComparator(boolean sortAsc, int mode) {
			bSortAsc = sortAsc;
			nMode = mode;
		}		
		public int compare(ContigGroupListData obj1, ContigGroupListData obj2) { return obj1.compareTo(obj2, bSortAsc, nMode); }
		
		private boolean bSortAsc;
		private int nMode;
	}
	
	private class QueryResultsTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 774460555629612058L;
		
		public QueryResultsTableModel(String [] columns, boolean [] visible) {
			changeColumns(columns, visible);
		}
		
		private String convertToCSV(Object val) {
			if(val == null) return "";
			if(val.getClass() == String.class) {
				if(((String)val).indexOf(',') >= 0) {
					return "\"" + ((String)val) + "\"";
				}
			}
			return val.toString();
		}
		
		public String getCSVExport() {
			String retVal = "";
			
			int x, y;
			String val = "";
			for(x=0; x<getRowCount(); x++) {
				for(y=0; y<getColumnCount()-1; y++) {
					if(getValueAt(x, y) != null)
						val = getValueAt(x, y).toString();
					else
						val = "";
					//Remove spaces if header
					if(x==0)
						val = val.replaceAll("\\s", "");
					retVal += convertToCSV(val) + ",";
				}
				if(getValueAt(x, y) != null)
					val = getValueAt(x, y).toString();
				else 
					val = "";
				//Remove spaces if header
				if(x==0)
					val = val.replaceAll("\\s", "");
				
				retVal += convertToCSV(val);
				if(x+1 < getRowCount()) 
					retVal += "\n";
			}
			
			return retVal;
		}
		
		public void changeColumns(String [] columns, boolean [] visible) {
			theColumns = new String[columns.length];
			for(int x=0; x<columns.length; x++)
				theColumns[x] = columns[x];
			
			theColumnSelections = new boolean[visible.length];
			for(int x=0; x<visible.length; x++)
				theColumnSelections[x] = visible[x];	
		}

		public int getColumnCount() {
	        	int retVal = 0;
	        	
	        	for(int x=0; x<theColumnSelections.length; x++)
	        		if(theColumnSelections[x])
	        			retVal++;
	        	
	        	return retVal;
        }

        public int getRowCount() {
	        	switch(theParentTab.getQueryMode()) {
	    		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
	    			if(!theParentTab.isGroupedSelected())
					return queryData.getNumIndividualHitSearchResults();
				else
					return queryData.getNumGroupHitSearchResults();
	    		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	
	    			if(!theParentTab.isGroupedSelected())	
					return queryData.getNumIndividualDescriptionSearchResults();
				else
					return queryData.getNumGroupDescriptionSearchResults();
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		
	    			return queryData.getNumContigIDSearchResults();
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		
	    			return queryData.getNumContigReadSearchResults();
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		
	    			return queryData.getNumContigNoteSearchResults();
	        	}
	        	return -1;
        }
        
        public Object getValueAt(int row, int col) {
        		int column = getMappedColumn(col);
	        	switch(theParentTab.getQueryMode()) {
	    		case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
	    			if(!theParentTab.isGroupedSelected())
					return queryData.getIndividualHitSearchResultAt(row, column);
				else
					return queryData.getGroupHitSearchResultAt(row, column);
	    		case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	
	    			if(!theParentTab.isGroupedSelected())
					return queryData.getIndividualDescriptionSearchResultAt(row, column);
				else
					return queryData.getGroupDescriptionSearchResultAt(row, column);
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		
	    			return queryData.getContigIDSearchResultAt(row, column);
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		
	    			return queryData.getContigReadSearchResultAt(row, column);
	    		case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		
	    			return queryData.getContigNoteSearchResultAt(row, column);
	        	}
	        	return null;
        }
        
        public String getColumnName(int col) { return theColumns[getMappedColumn(col)]; }
        
        private int getMappedColumn(int column) {
	        	int colIndex = 0;
	        	int retVal = -1;
	        	
	        	for(int x=0; x<theColumnSelections.length && retVal < 0; x++) {
	        		if(theColumnSelections[x] && (column == colIndex)) {
	        			retVal = x;
	        		}
	        		if(theColumnSelections[x])
	        			colIndex++;
	        	}
	        	return retVal;
        }
                
  	  	public class ColumnListener extends MouseAdapter {
		    protected JTable table;

		    public ColumnListener(JTable t) {
		    	table = t;
		    	
		    	bAscend = new boolean[table.getColumnCount()];
		    	for(int x=0; x<bAscend.length; x++)
		    		bAscend[x] = true;
		    }

		    public void mouseClicked(MouseEvent e) {
		    	sortTable(e.getX(), SwingUtilities.isLeftMouseButton(e));						
		    }
  	  	
  	  		private void sortTable(int xLocation, boolean leftclick) {
  	  			TableColumnModel colModel = table.getColumnModel();
  	  			int columnModelIndex = (colModel.getColumnIndexAtX(xLocation));
  	  			int modelIndex = (colModel.getColumn(columnModelIndex).getModelIndex());
	    	
  	  			if (modelIndex < 0)
  	  				return;
  	  			
  	  			int mappedColumn = getMappedColumn(modelIndex);
  	  			  	  			
  	  			if(leftclick)
  	  				bAscend[modelIndex] = !bAscend[modelIndex];
  	  			else
  	  				bAscend[modelIndex] = true;
	    	
  	  			switch(theParentTab.getQueryMode()) {
  	  			case BasicPromptPanel.QUERY_MODE_HIT_ID: 			
  	  				if(!theParentTab.isGroupedSelected())
						Collections.sort(queryData.resultIndividualHitList, new HitIndividualListComparator(bAscend[modelIndex], mappedColumn));
					else
						Collections.sort(queryData.resultGroupHitList, new HitGroupListComparator(bAscend[modelIndex], mappedColumn));
					break;
  	  			case BasicPromptPanel.QUERY_MODE_HIT_DESCRIPTION:	
  	  				if(!theParentTab.isGroupedSelected())
						Collections.sort(queryData.resultIndividualDescriptionList, new HitIndividualListComparator(bAscend[modelIndex], mappedColumn));
					else
						Collections.sort(queryData.resultGroupDescriptionList, new HitGroupListComparator( bAscend[modelIndex], mappedColumn));
					break;
  	  			case BasicPromptPanel.QUERY_MODE_CONTIG_NAME:		
  	  				Collections.sort(queryData.resultContigIDList, new ContigGroupListComparator(bAscend[modelIndex], mappedColumn));
    					break;
  	  			case BasicPromptPanel.QUERY_MODE_CONTIG_READ:		
  	  				Collections.sort(queryData.resultContigReadList, new ContigIndividualListComparator(bAscend[modelIndex], mappedColumn));
    					break;
  	  			case BasicPromptPanel.QUERY_MODE_CONTIG_NOTE:		
  	  				Collections.sort(queryData.resultContigNoteList, new ContigGroupListComparator(bAscend[modelIndex], mappedColumn));
  	  			}
  	  			
  	  			table.tableChanged(new TableModelEvent(QueryResultsTableModel.this));
  	  			table.repaint();
  	  		}
  	  		
  	  		private boolean [] bAscend = null;
  	  	}
        private String [] theColumns = null;
		private boolean [] theColumnSelections = null;		
	}
	
	private class SelectionListener implements ListSelectionListener {
		public SelectionListener(JTable table, BasicQueryTab parentTab) {
			theTable = table;
			theParentTab = parentTab;
		}
		
		public void valueChanged(ListSelectionEvent arg0) {
			if(theTable.getSelectedRowCount() >= 1)
				theParentTab.setViewContigEnabled(true);
			else
				theParentTab.setViewContigEnabled(false);
		}
		
		private JTable theTable = null;
		private BasicQueryTab theParentTab = null;
	}
	
    private class MyCellRenderer extends JLabel implements TableCellRenderer {
        private static final long serialVersionUID = -5492378832471357205L;

        public Component getTableCellRendererComponent(JTable arg0, Object arg1, boolean arg2, boolean arg3, int row, int col) {
                String theLabel = "";
                if(arg1 != null) {
                        if(arg1 instanceof Double) {
                                if(((Double)arg1) == 0)
                                        theLabel = "0";
                                else
                                        theLabel = (new DecimalFormat("0.0E0")).format((Double)arg1);
                        } else {
                                theLabel = arg1.toString();
                        }
                }
                setText(theLabel);
                Font f = this.getFont();
                setFont(new Font(f.getName(), Font.PLAIN, f.getSize()));
                if(row % 2 == 1)
                	setBackground(Color.RED);
                return this;
        }
    }	
    
    private static int compareStrings(String str1, String str2) {
	    	if(str1 == null && str2 == null)
	    		return 0;
	    	if(str1 == null)
	    		return -1;
	    	if(str2 == null)
	    		return 1;
	    	return str1.compareTo(str2);
    }
}
